package org.springframework.boot.autoconfigure.netty.support;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.netty.support.JodaTimeUtil;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.util.function.Function;

public class JwtUtils {

    private static Map<String, Field> fieldMap = new HashMap<>();

    /**
     * 私钥加密token
     *
     * @param JWTInfo      载荷中的数据
     * @param privateKey    私钥
     * @param expireMinutes 过期时间，单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(JWTInfo JWTInfo, PrivateKey privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, JWTInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, JWTInfo.getUsername())
                .setExpiration(JodaTimeUtil.plusMinutes(localDateTimeConvertToDate(),expireMinutes))
                .signWith(SignatureAlgorithm.RS256, privateKey)
                .compact();
    }

    /**
     * 私钥加密token
     *
     * @param JWTInfo      载荷中的数据
     * @param privateKey    私钥字节数组
     * @param expireMinutes 过期时间，单位秒
     * @return
     * @throws Exception
     */
    public static String generateToken(JWTInfo JWTInfo, byte[] privateKey, int expireMinutes) throws Exception {
        return Jwts.builder()
                .claim(JwtConstans.JWT_KEY_ID, JWTInfo.getId())
                .claim(JwtConstans.JWT_KEY_USER_NAME, JWTInfo.getUsername())
                .setExpiration(JodaTimeUtil.plusMinutes(localDateTimeConvertToDate(),expireMinutes))
                .signWith(SignatureAlgorithm.RS256, RsaUtils.getPrivateKey(privateKey))
                .compact();
    }


    /**
     * 判断token是否存在与有效
     *
     * @param accessToken
     * @return
     */
    public static boolean validatorToken(String accessToken,PublicKey publicKey) {
        try {
            if (StringUtils.isEmpty(accessToken)) {
                return false;
            }
            parserToken(accessToken,publicKey);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, PublicKey publicKey) {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token);
    }

    /**
     * 公钥解析token
     *
     * @param token     用户请求中的token
     * @param publicKey 公钥字节数组
     * @return
     * @throws Exception
     */
    private static Jws<Claims> parserToken(String token, byte[] publicKey) throws Exception {
        return Jwts.parser().setSigningKey(RsaUtils.getPublicKey(publicKey))
                .parseClaimsJws(token);
    }

    /**
     * 获得token中的自定义信息
     */
    public static String getClaimFiled(String token, String filed,PublicKey publicKey) {
        try {
            return String.valueOf(Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody().get(filed));
        } catch (Exception e) {
            return null;
        }
    }


    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static JWTInfo getInfoFromToken(String token, PublicKey publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return generateJWTInfo(body);
    }

    /**
     * 获取token中的用户信息
     *
     * @param token     用户请求中的令牌
     * @param publicKey 公钥
     * @return 用户信息
     * @throws Exception
     */
    public static JWTInfo getInfoFromToken(String token, byte[] publicKey) throws Exception {
        Jws<Claims> claimsJws = parserToken(token, publicKey);
        Claims body = claimsJws.getBody();
        return generateJWTInfo(body);
    }

    private static JWTInfo generateJWTInfo(Claims claims){
        JWTInfo jwtInfo = new JWTInfo();

        ReflectionUtils.doWithFields(JWTInfo.class,field -> {
            fieldMap.computeIfAbsent(field.getName(), new Function<String, Field>() {
                @Override
                public Field apply(String s) {
                    return field;
                }
            });
        });

        claims.keySet().stream().forEach(key ->{
            if(fieldMap.containsKey(key)){
                Object o = claims.get(key);
                Field field = fieldMap.get(key);
                ReflectionUtils.makeAccessible(field);
                switch (key){
                    case "id" :
                        try {
                            field.set(jwtInfo,ObjectUtils.toLong(o));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        break;
                    case "username" :
                        try {
                            field.set(jwtInfo,ObjectUtils.toString(o));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        break;
                    case "authorities" :
                        List<String> authorities = (List<String>) o;
                        Authentication authentication = new Authentication();
                        Set<String> roles = new HashSet<>(16);
                        Set<String > auths = new HashSet<>(16);
                        authorities.forEach(auth ->{
                            if(StringUtils.startsWith(auth,"ROLE_")){
                                roles.add(auth);
                            }else {
                                auths.add(auth);
                            }
                        });
                        if(CollectionUtils.isEmpty(roles)){
                            roles.add("ROLE_ANONYMOUS");
                        }

                        if(CollectionUtils.isEmpty(auths)){
                            auths.add("ANONYMOUS");
                        }
                        authentication.setPermissions(auths);
                        authentication.setRoles(roles);
                        try {
                            field.set(jwtInfo,authentication);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        });

        return jwtInfo;
    }

    //将java.util.Date 转换为java8 的java.time.LocalDateTime,默认时区为东8区
    private static LocalDateTime dateConvertToLocalDateTime(Date date) {
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
    }

    private static LocalDateTime dateConvertToLocalDateTime() {
        Date date = new Date();
        return date.toInstant().atOffset(ZoneOffset.of("+8")).toLocalDateTime();
    }


    //将java8 的 java.time.LocalDateTime 转换为 java.util.Date，默认时区为东8区
    private static Date localDateTimeConvertToDate(LocalDateTime localDateTime) {
        return Date.from(localDateTime.toInstant(ZoneOffset.of("+8")));
    }

    private static Date localDateTimeConvertToDate() {

        LocalDateTime localDateTime = LocalDateTime.now();
        return Date.from(localDateTime.toInstant(ZoneOffset.of("+8")));
    }
}